package top.lyjwn.todo.webSocket;

import com.alibaba.fastjson2.JSONException;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.JSONValidator;
import com.alibaba.fastjson2.schema.JSONSchema;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import top.lyjwn.todo.aop.token.Login;
import top.lyjwn.todo.aop.token.Token;
import top.lyjwn.todo.common.template.R;
import top.lyjwn.todo.webSocket.data.Content;
import top.lyjwn.todo.webSocket.data.Message;
import top.lyjwn.todo.webSocket.data.WebSocketUser;
import top.lyjwn.todo.webSocket.service.WebSocketMessageService;
import top.lyjwn.todo.webSocket.service.WebSocketUserService;

import jakarta.websocket.*;
import jakarta.websocket.server.ServerEndpoint;

import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;

@ServerEndpoint(value = "/ws/connect", configurator = GetRequestInfoConfigurator.class)
@Component
public class WebSocketServer {

    private final Logger log = LoggerFactory.getLogger("WebSocketServer");

    /**
     * 在线的客户端
     */
    private static Map<String, Map<String, Session>> clients = new ConcurrentHashMap<>();

    /**
     * WebSocket 任务处理队列
     */
    private final static ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();


    private static Token token;

    private static WebSocketUserService webSocketUserService;

    private static WebSocketMessageService webSocketMessageService;

    private String tokenString;

    private String userId;


    @OnOpen
    public void onOpen(Session session, EndpointConfig config) {
        //将新用户存入在线的组
        session.setMaxIdleTimeout(30 * 1000);
        Object userPropertiesHeaders = config.getUserProperties().get("headers");
        if (userPropertiesHeaders != null) {
            Map<String, List<String>> headers = (Map<String, List<String>>) userPropertiesHeaders;
            log.info("webSocket: connect", headers.get("Host").get(0));
            if (headers.get("token") != null && headers.get("token").size() > 0) {
                String token = headers.get("token").get(0);
                Login login = WebSocketServer.token.getLoginByToken(Login.class, token);
                Map<String, Session> tokenSession = new ConcurrentHashMap<>();
                //session记录用户信息
//                session.getUserProperties().put("token",token);
//                session.getUserProperties().put("userId",login.getFkUser());
                this.tokenString = token;
                this.userId = login.getUserId();
                tokenSession.put(token, session);
                getClients().put(login.getUserId(), tokenSession);
                return;
            }
        }
        try {
            session.close();
        } catch (IOException e) {
            log.error("webSocket - connect exception close fail:" + e.getMessage());
        }

    }

    /**
     * 客户端关闭
     */
    @OnClose
    public void onClose() {
        log.info("webSocket - close: " + this.userId + " " + this.tokenString);
        //将掉线的用户移除在线的组里
        kickOut();
    }

    /**
     * 发生错误
     *
     * @param throwable e
     */
    @OnError
    public void onError(Throwable throwable) {
        log.info("webSocket - error: " + throwable.getMessage() + " " + this.userId + " " + this.tokenString);
//        kickOut();

    }

    /**
     * 收到客户端发来消息
     * <聊天消息>
     * {
     * "type": 0,
     * "content":"",
     * "receiver":"userId",
     * "sendTime":"2020-05-20 00:00:00",
     * "token":"token",
     * "messageClientId":"messageClientId"
     * }
     * <已读标记>
     * {
     * "type": 2,
     * "id": "message_id",
     * }
     * <心跳>
     * {
     * "type": 3,
     * "token":"token"
     * }
     *
     * @param messageStr 消息对象
     */
    @OnMessage
    public void onMessage(String messageStr) {
        if (webSocketUserService == null) return;
        JSONObject jsonObject = null;
        try {
            jsonObject = JSONObject.parseObject(messageStr);
        } catch (JSONException jex) {
            return;
        }
        try {

            int type = jsonObject.getIntValue("type");
            if (type != Message.PONG) {
                log.info("webSocket - message came in client: " + messageStr);
            }
            //转发聊天消息
            if (type != Message.PONG && type != Message.READ) {
                Login login = this.token.getLoginByToken(Login.class, jsonObject.getString("token"));
                R<WebSocketUser> senderUser = webSocketUserService.findById(login.getUserId());
                if (senderUser.isDone()) {
                    WebSocketUser senderUserEntity = senderUser.getData();
                    WebSocketUser sender = new WebSocketUser(senderUserEntity.getId());
                    sender.setContactAddress(senderUserEntity.getContactAddress());
                    sender.setContactTel(senderUserEntity.getContactTel());
                    sender.setEmail(senderUserEntity.getEmail());
                    sender.setName(senderUserEntity.getName());
                    sender.setSex(senderUserEntity.getSex());
                    sender.setPicturePath(senderUserEntity.getPicturePath());
                    R<WebSocketUser> receiverUser = webSocketUserService.findById(jsonObject.getString("receiver"));
                    if (receiverUser.isDone()) {
                        WebSocketUser receiver = getWebSocketUser(receiverUser, senderUserEntity);
                        Message message = new Message(jsonObject.getString("messageClientId"), sender, receiver, Message.TEXT);
                        Content content = new Content();
                        switch (type) {
                            case Message.TEXT:
                                //文字类
                                content.setText(jsonObject.getString("content"));
                                break;
                            case Message.IMAGE:
                                //图片类
                                content.setUrl(jsonObject.getString("content"));
                                break;
                        }
                        message.setContent(content);
                        message.setSenderTime(jsonObject.getDate("sendTime"));
                        if (webSocketMessageService != null) {
                            if (webSocketMessageService.onMessage(message)) {
                                String messageJson = JSONObject.toJSONString(message);
                                send(message.getReceiver().getId(), messageJson);
                            }
                        } else {
                            String messageJson = JSONObject.toJSONString(message);
                            send(message.getReceiver().getId(), messageJson);
                        }
                        /**
                         * 消息业务不为空时保存信息
                         */
                        executor.execute(new Runnable() {
                            @Override
                            public void run() {
                                if (webSocketMessageService != null) {
                                    webSocketMessageService.save(message);
                                }
                            }
                        });

                    }
                }
            } else {
                //系统事件消息
                switch (type) {
                    case Message.READ:
                        //已读标记
                        if (webSocketMessageService != null) {
                            webSocketMessageService.read(jsonObject.getString("id"));
                        }
                        break;
                    case Message.PONG:
                        //心跳
                        Login login = this.token.getLoginByToken(Login.class, jsonObject.getString("token"));
                        Message message = new Message(null, null, null, Message.PONG);
                        send(login.getUserId(), JSONObject.toJSONString(message));
                        break;
                }
            }
        } catch (JSONException e) {
            log.error("webSocket - JSONException: " + e.getMessage() + "  data:" + messageStr);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static WebSocketUser getWebSocketUser(R<WebSocketUser> receiverUser, WebSocketUser senderUserEntity) {
        WebSocketUser receiverUserEntity = receiverUser.getData();
        WebSocketUser receiver = new WebSocketUser(receiverUserEntity.getId());
        receiver.setContactAddress(senderUserEntity.getContactAddress());
        receiver.setContactTel(senderUserEntity.getContactTel());
        receiver.setEmail(senderUserEntity.getEmail());
        receiver.setName(senderUserEntity.getName());
        receiver.setSex(senderUserEntity.getSex());
        receiver.setPicturePath(senderUserEntity.getPicturePath());
        return receiver;
    }

    /**
     * 踢出客户端
     */
    private void kickOut() {
        log.info("webSocket - kickOut");
        if (getClients().get(this.userId) != null) {
            if (getClients().get(this.userId).get(this.tokenString) != null) {
                try {
                    getClients().get(this.userId).get(this.tokenString).close();
                } catch (IOException e) {
                    log.error("webSocket - error exception close fail:" + e.getMessage());
                }
            }
            getClients().get(this.userId).remove(this.tokenString);
        }
        if (getClients().get(this.userId) != null && getClients().get(this.userId).keySet().size() == 0) {
            getClients().remove(this.userId);
        }
    }

    /**
     * 单发消息
     *
     * @param message 消息内容
     */
    public static void send(String userId, String message) {
        Map<String, Session> tokenSessionMap = getClients().get(userId);
        for (Map.Entry<String, Session> sessionEntry : tokenSessionMap.entrySet()) {
            sessionEntry.getValue().getAsyncRemote().sendText(message);
        }
    }

    /**
     * 群发消息
     *
     * @param message 消息内容
     */
    public static void sendAll(String message) {
        for (Map.Entry<String, Map<String, Session>> tokenSession : getClients().entrySet()) {
            Map<String, Session> tokenSessionMap = tokenSession.getValue();
            for (Map.Entry<String, Session> sessionEntry : tokenSessionMap.entrySet()) {
                sessionEntry.getValue().getAsyncRemote().sendText(message);
            }
        }
    }


    public static Map<String, Map<String, Session>> getClients() {
        return clients;
    }

    @Autowired
    public void setToken(Token token) {
        if (this.token == null) {
            this.token = token;
        }
    }

    @Autowired(required = false)
    public void setWebSocketUserService(WebSocketUserService webSocketUserService) {
        if (this.webSocketUserService == null) {
            this.webSocketUserService = webSocketUserService;
        }
    }

    @Autowired(required = false)
    public void setWebSocketMessageService(WebSocketMessageService webSocketMessageService) {
        if (this.webSocketMessageService == null) {
            this.webSocketMessageService = webSocketMessageService;
        }
    }
}
